﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using TWiStEr.Math;

namespace Phys.Model.Affectors {
	public class GravityAffector : IAffector {
		/// <summary>
		/// Gravitational constant
		/// Measured in m^3 * kg^-1 * s^-2 = N(m/kg)^2
		/// Precision: +-0.00067
		/// </summary>
		public const double G = 6.67428e-11;

		/// <summary>
		/// Creates a gravity affector which represents the Earth's gravity field
		/// </summary>
		public GravityAffector() : this(5.9736e+24, 6.37101e+6) { }

		/// <summary>
		/// Creates a gravity affector with the given parameters.
		/// </summary>
		/// <param name="planetMass">mass of the planet which is attracts the body</param>
		/// <param name="distanceFromCenter">distance of the planet's center and body's center</param>
		public GravityAffector(double planetMass, double distanceFromCenter) {
			PlanetMass = planetMass;
			DistanceFromPlanetCenter = distanceFromCenter;
		}

		/// <summary>
		/// Cache of the gravitational acceleration "constant"
		/// Measured in m/s^2
		/// </summary>
		private double _g;
		/// <summary>
		/// Gets the Gravity acceleration on this planet
		/// Measured in m/s^2
		/// </summary>
		public double GravityAcceleration { get { return _g; } }
		/// <summary>
		/// Caches the gravity acceleration for later use in Affect
		/// </summary>
		/// <param name="planetMass">mass of the planet which is attracts the body</param>
		/// <param name="distanceFromCenter">distance of the planet's center and body's center</param>
		private void SetGravityAcceleration(double planetMass, double distanceFromCenter) {
			_g = G * planetMass / Math.Pow(distanceFromCenter, 2);
		}

		private double _planetMass;
		/// <summary>
		/// Gets or sets the mass of the planet which affects the objects.
		/// Measured in kilogramms.
		/// </summary>
		public double PlanetMass {
			get { return _planetMass; }
			set {
				_planetMass = value;
				SetGravityAcceleration(value, DistanceFromPlanetCenter);
			}
		}

		private double _distance;
		/// <summary>
		/// Gets or sets distance from the planet center to the center of the object which is affected.
		/// Measured in meters.
		/// This is usually the average radius of the sphere-like planet.
		/// </summary>
		public double DistanceFromPlanetCenter {
			get { return _distance; }
			set {
				_distance = value;
				SetGravityAcceleration(PlanetMass, value);
			}
		}

		public void Affect(IObject o, double seconds) {
			o.Forces += new Vector(0, o.Mass * -_g, 0);
		}
	}
}
